package com.spzx.order.service.impl;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.cart.api.RemoteCartService;
import com.spzx.cart.api.domain.CartItem;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.utils.StringUtils;
import com.spzx.common.core.utils.uuid.IdUtils;
import com.spzx.common.security.utils.SecurityUtils;
import com.spzx.order.domain.OrderItem;
import com.spzx.order.domain.vo.OrderTradeVO;
import com.spzx.order.service.IOrderItemService;
import com.spzx.order.service.IOrderLogService;
import com.spzx.product.api.RemoteProductService;
import com.spzx.product.api.domain.dto.OrderStockDTO;
import com.spzx.user.api.RemoteUserInfoService;
import com.spzx.user.api.domain.UserAddress;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.spzx.order.mapper.OrderInfoMapper;
import com.spzx.order.domain.OrderInfo;
import com.spzx.order.service.IOrderInfoService;

/**
 * 订单Service业务层处理
 *
 * @author atguigu
 * @date 2024-07-03
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements IOrderInfoService {
    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Resource
    IOrderLogService orderLogService;
    @Resource
    RemoteCartService remoteCartService;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    RemoteProductService remoteProductService;

    @Resource
    RemoteUserInfoService remoteUserInfoService;

    @Resource
    IOrderItemService orderItemService;

//
//    /**
//     * 查询订单列表
//     *
//     * @param orderInfo 订单
//     * @return 订单
//     */
//    @Override
//    public List<OrderInfo> selectOrderInfoList(OrderInfo orderInfo)
//    {
//        return orderInfoMapper.selectOrderInfoList(orderInfo);
//    }

//    @Override
//    public List<OrderInfo> selectList(OrderInfo orderInfo) {
//
//
//
//
//        return null;
//    }

    @Override
    public OrderTradeVO trade() {
        R<List<CartItem>> listR = remoteCartService.getCheckedCartItems();
        if (listR.getCode() != R.SUCCESS) {
            throw new ServiceException("查询购物车数据失败");
        }
        List<OrderItem> orderItemList = listR.getData().stream().map(cartItem -> {
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderId(null);
            orderItem.setSkuId(cartItem.getSkuId());
            orderItem.setSkuName(cartItem.getSkuName());
            orderItem.setThumbImg(cartItem.getThumbImg());
            orderItem.setSkuPrice(cartItem.getSkuPrice());
            orderItem.setSkuNum(cartItem.getSkuNum().longValue());
            return orderItem;
        }).collect(Collectors.toList());

        //创建并设置属性值给 结算页对象
        OrderTradeVO orderTradeVO = new OrderTradeVO();
        orderTradeVO.setOrderItemList(orderItemList);
        orderTradeVO.setTradeNo(IdUtils.fastSimpleUUID());
        BigDecimal totalAmount = orderItemList.stream()
                //获取所有购物项的单个分别计算小计金额 最后累加
                .map(orderItem -> orderItem.getSkuPrice()
                        .multiply(new BigDecimal(orderItem.getSkuNum())))
                .reduce((a, b) -> a.add(b)).get();
        orderTradeVO.setTotalAmount(totalAmount);

        return orderTradeVO;


    }

    @Override
    public String submitOrder(OrderInfo orderInfo) {
        /*                   <下单>
                    在结算页 点击 提交订单
                  1、确认订单商品价格
                     2、确认库存是否足够(验库存锁库存)
                         3、创建订单保存
	                            订单：
	                            订单项：
                            4、删除订单涉及的购物车 购物项
                                5、记录订单创建的日志
 */
        //1
        List<OrderItem> orderItems = orderInfo.getOrderItemList();
        if (StringUtils.isEmpty(orderItems)) {
            throw new ServiceException("没有选中要购买的商品");
        }
        //0 、幂等性处理： 防止表单重复提交
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("spzx:order:trade:" + orderInfo.getOrderNo(), "1", 20, TimeUnit.MINUTES);
        if (!flag) { //非空校验
            throw new ServiceException("请勿频繁提交！");
        }
        //        1、确认订单商品价格 : 商品价格存储在product_sku表中
        // 查询订单中所有商品数据库中的单价 和 订单里面订单项的单价比较
        List<Long> skuIds = orderItems.stream().map(orderItem ->
                orderItem.getSkuId()).collect(Collectors.toList());

        R<Map<Long, BigDecimal>> skusPriceR = remoteProductService.getSkusPrice(skuIds);

        if (skusPriceR.getCode() != R.SUCCESS) {
            throw new ServiceException(skusPriceR.getMsg());
        }
        //获取数据库中查询到的sku的价格
        Map<Long, BigDecimal> skusPriceRData = skusPriceR.getData();
        //使用提交订单中的价格  和数据库查询到的价格比较
        orderItems.forEach(orderItem -> {
                    if (orderItem.getSkuPrice().compareTo(skusPriceRData.get(orderItem.getSkuId())) != 0) ;//比较失败
                    //同步redis缓存的购物车的价格:
                    // 修改购物车商品的价格
                    throw new ServiceException("页面数据过期，请刷新");
                }
        );

        //        2、确认库存是否足够(验库存锁库存)： 库存存在 sku_stock表中
        // 将 一组skuId 和要它要购买的数量  提交给product服务 去验库存锁库存
        //自定义DTO类： 用来携带验库存锁库存的参数 和响应的信息
        // skuId ： 商品id , skuNum  要购买的数量,  flag 校验是否成功  ,  userId
        //2.1 获取要验库存的 数据集合
        List<OrderStockDTO> stockDTOS = orderItems.stream().map(orderItem -> {
            OrderStockDTO orderStockDTO = new OrderStockDTO();
            orderStockDTO.setUserId(SecurityUtils.getUserId());
            orderStockDTO.setSkuId(orderItem.getSkuId());
            orderStockDTO.setSkuName(orderItem.getSkuName());
            orderStockDTO.setSkuNum(orderItem.getSkuNum().intValue());
            return orderStockDTO;
        }).collect(Collectors.toList());

        //2.2 远程访问验库存接口
        //  库存表：
        //        total_num 总库存 lock_num+available_num,
        //        lock_num 锁定的库存(下单待支付),
        //        available_num 可用库存(用户提交订单时 还剩余多少库存  total_num-lock_num) ,
        //        sale_num  销量
        //       用户提交订单时，锁定库存：  lock_num+ skuNum ， available_num - skuNum
        //       用户取消订单，释放库存：   lock_num-  skuNum ， available_num + skuNum
        //      用户支付成功： total_num - skuNum  lock_num - sku_num


        R<List<OrderStockDTO>> lockStockR = remoteProductService.checkAndLockStock(stockDTOS);
        if (lockStockR.getCode() != R.SUCCESS) {
            throw new ServiceException(lockStockR.getMsg());
        }

        //拼接 库存校验失败的商品信息 抛出异常
        stockDTOS = lockStockR.getData();
        StringBuilder sb = new StringBuilder("");
        boolean checkStock = true;
        for (int i = 0; i < stockDTOS.size(); i++) {
            if (!stockDTOS.get(i).getFlag()) {
                checkStock = false;//库存校验失败
                sb.append(stockDTOS.get(i).getSkuName() + " ");
            }
        }
        //库存校验失败
        if (!checkStock) {
            throw new ServiceException("订单中 " + sb.toString() + " 库存不足");
        }

        //        3、创建订单保存
        //订单的收货人地址信息

        R<UserAddress> addressR = remoteUserInfoService.getInfo(orderInfo.getUserAddressId());
        if (addressR.getCode() != R.SUCCESS) {
            throw new ServiceException(addressR.getMsg());
        }

        orderInfo.setUserId(SecurityUtils.getUserId());
        orderInfo.setNickName(SecurityUtils.getUsername());
        orderInfo.setOrderNo(UUID.randomUUID().toString().replace("-", ""));
//运费+商品总金额 - 优惠券金额 = 订单总金额
        BigDecimal skusAmount = orderItems.stream().map(orderItem -> new BigDecimal(orderItem.getSkuNum())
                .multiply(orderItem.getSkuPrice())).reduce((a, b) -> a.add(b)).get();

        //订单总金额
        orderInfo.setTotalAmount(orderInfo.getFeightFee().add(skusAmount).subtract(new BigDecimal(0)));
        //优惠券金额
        orderInfo.setCouponAmount(new BigDecimal(0));


        //优惠前价格
        orderInfo.setOriginalTotalAmount(orderInfo.getFeightFee().add(skusAmount));
        orderInfo.setOrderStatus(0L);


        //设置地址信息
        orderInfo.setReceiverName(addressR.getData().getName());
        orderInfo.setReceiverPhone(addressR.getData().getPhone());
        orderInfo.setReceiverTagName(addressR.getData().getTagName());
        orderInfo.setReceiverProvince(addressR.getData().getProvinceCode());
        orderInfo.setReceiverCity(addressR.getData().getCityCode());
        orderInfo.setReceiverDistrict(addressR.getData().getDistrictCode());
        orderInfo.setReceiverAddress(addressR.getData().getFullAddress());
        this.save(orderInfo);

        Long infoId = orderInfo.getId();
        orderItems.forEach(orderItem -> {
            orderItem.setOrderId(infoId);

        });
        orderItemService.saveBatch(orderItems);


        //        4、删除订单涉及的购物车中选中的购物项： 操作cart服务当前用户的购物车
        //删除购物车的操作：就算失败了 对提交订单的业务影响不大
        remoteCartService.delCheckedCartItems();

        //        5、异步保存订单创建的日志

        orderLogService.saveLogAsync(orderInfo);
        return orderInfo.getId().toString();
    }

    @Override
    public Page<OrderInfo> userOrderInfoList(Integer pageNum, Integer pageSize, Integer orderStatus)
    {
        Long userId = SecurityUtils.getUserId();
        Page<OrderInfo> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<OrderInfo> queryWrapper = Wrappers.lambdaQuery(OrderInfo.class).eq(OrderInfo::getUserId, userId);
        if (queryWrapper!=null){
            Wrappers.lambdaQuery(OrderInfo.class)
                    .eq(OrderInfo::getOrderStatus,orderStatus);
        }
        page = this.page(page, queryWrapper);
        page.getRecords().forEach(orderInfo->{
            List<OrderItem> orderItems = orderItemService.list(Wrappers.lambdaQuery(OrderItem.class)
                    .eq(OrderItem::getOrderId, orderInfo.getId()));

            orderInfo.setOrderItemList(orderItems);

        });


        return page;
    }

    @Override
    public OrderInfo selectOrderInfoById(Long orderId) {
        OrderInfo orderInfo = this.getById(orderId);
        List<OrderItem> orderItems = orderItemService.list(Wrappers.lambdaQuery(OrderItem.class)
                .eq(OrderItem::getOrderId, orderInfo.getId()));
        orderInfo.setOrderItemList(orderItems);

        return orderInfo;
    }

}
